package com.tidy.seed.admin.system.service;

import com.tidy.seed.admin.system.entity.Log;
import com.tidy.seed.util.UserThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 异常日志业务处理服务（专注于异常日志的组装、脱敏、异步保存等）
 */
@Service
public class ErrorLogService {

    private static final Logger log = LoggerFactory.getLogger(ErrorLogService.class);
    private static final int MAX_BODY_LENGTH = 2048; // 请求体最大长度限制

    @Resource
    private LogService logService; // 依赖基础CRUD服务

    @Resource
    private ThreadPoolTaskExecutor logExecutor; // 注入线程池（需提前配置）

    /**
     * 保存异常日志入口方法
     */
    public void saveErrorLog(HttpServletRequest request, Integer statusCode, String message) {
        Log logEntity = buildLogEntity(request, statusCode, message, 0);
        saveLogAsync(logEntity); // 异步保存
    }

    public void saveApiLog(HttpServletRequest request, Integer statusCode, String message, Integer type) {
        Log logEntity = buildLogEntity(request, statusCode, message, 1);
        saveLogAsync(logEntity); // 异步保存
    }


    /**
     * 构建日志实体（组装请求信息）
     */
    private Log buildLogEntity(HttpServletRequest request, Integer statusCode, String message, Integer type) {
        Log logEntity = new Log();

        // 1. 设置用户标识
        Integer userId = UserThreadLocal.getUserId();
        logEntity.setUserFlag(userId != null ? String.valueOf(userId) : "anonymous");

        // 2. 基础请求信息
        logEntity.setIp(request.getRemoteAddr());
        logEntity.setName(type == 1 ? "接口日志" : "错误日志");
        logEntity.setMethod(request.getMethod());
        logEntity.setPath(request.getRequestURI());

        // 3. 处理请求体（含脱敏和截断）
        String requestBody = handleRequestBody(request);

        // 4. 组装请求参数（路径变量/请求体/查询参数）
        String args = buildRequestArgs(request, requestBody);
        logEntity.setArguments(args);

        // 5. 其他日志信息
        logEntity.setStatus(statusCode);
        logEntity.setMessage(message);
        logEntity.setType(type); // 0代表错误日志 1代表接口日志
        logEntity.setCreateAt(LocalDateTime.now());

        return logEntity;
    }

    /**
     * 处理请求体（读取+脱敏+截断）
     */
    private String handleRequestBody(HttpServletRequest request) {
        String requestBody = "";
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request;
            byte[] buf = cachingRequest.getContentAsByteArray();

            if (buf.length > 0) {
                try {
                    // 处理编码
                    String encoding = cachingRequest.getCharacterEncoding();
                    // 转换为字符串
                    String rawBody = new String(buf, encoding);
                    // 截断过长内容
                    if (rawBody.length() > MAX_BODY_LENGTH) {
                        rawBody = rawBody.substring(0, MAX_BODY_LENGTH) + "...[内容已截断]";
                    }
                    // 敏感信息脱敏
                    requestBody = maskSensitiveInfo(rawBody);

                } catch (UnsupportedEncodingException e) {
                    requestBody = "[请求体解析失败：编码不支持]";
                    log.warn("请求体编码解析失败", e);
                }
            } else {
                requestBody = "[请求体为空]";
                log.debug("请求体为空，请求方法: {}, URI: {}", request.getMethod(), request.getRequestURI());
            }
        } else {
            requestBody = "[请求未被ContentCachingRequestWrapper包装]";
            log.warn("无法读取请求体：请求未被正确包装，URI: {}", request.getRequestURI());
        }
        return requestBody;
    }

    /**
     * 组装请求参数（优先级：路径变量 > 请求体 > 查询参数）
     */
    private String buildRequestArgs(HttpServletRequest request, String requestBody) {
        // 获取路径变量
        @SuppressWarnings("unchecked")
        Map<String, String> pathVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);

        if (pathVariables != null && !pathVariables.isEmpty()) {
            return "路径变量: " + pathVariables;
        } else if (!requestBody.isEmpty() && !"[请求体为空]".equals(requestBody)) {
            return "请求体: " + requestBody;
        } else {
            String queryString = request.getQueryString();
            return queryString != null ? "查询参数: " + queryString : "[无请求参数]";
        }
    }

    /**
     * 敏感信息脱敏（过滤密码、token等）
     */
    private String maskSensitiveInfo(String content) {
        if (content == null || content.isEmpty()) {
            return "";
        }

        // 敏感字段列表，可根据业务扩展
        String[] sensitiveFields = {"password", "pwd", "token", "accessToken"};

        String maskedContent = content;
        for (String field : sensitiveFields) {
            // 处理JSON格式（如 "password":"123456"）
            maskedContent = maskedContent.replaceAll(
                    "\"" + field + "\":\"[^\"]+\"",
                    "\"" + field + "\":\"***\""
            );
            // 处理表单格式（如 password=123456）
            maskedContent = maskedContent.replaceAll(
                    field + "=[^&]+",
                    field + "=***"
            );
        }
        return maskedContent;
    }

    /**
     * 异步保存日志（失败时降级为同步）
     */
    private void saveLogAsync(Log logEntity) {
        try {
            logExecutor.execute(() -> {
                try {
                    logService.save(logEntity); // 调用基础CRUD服务保存
                    log.debug("异常日志保存成功，ID: {}", logEntity.getId());
                } catch (Exception e) {
                    log.error("异步保存异常日志失败", e);
                }
            });
        } catch (Exception e) {
            // 异步执行失败，降级为同步保存
            log.warn("异步保存日志失败，尝试同步保存", e);
            try {
                logService.save(logEntity);
            } catch (Exception ex) {
                log.error("同步保存日志也失败", ex);
            }
        }
    }
}